/*
 * Copyright (c) United Binary LLC.  All rights reserved.
 * 
 * This code is licensed under the MIT License
 * 
 * SEE: http://harnessit.codeplex.com/license
 * 
 */
#region using ...
using System;
using System.Collections;
using UnitedBinary.UnitTests.Framework;
using UnitedBinary.HarnessIt.SharedTypes;
using UnitedBinary.HarnessIt.UnitTests.Settings;
#endregion

namespace UnitedBinary.HarnessIt.UnitTests.SharedTypes
{
	/// <summary>
	///		Unit tests for the Method class in the SharedTypes assembly.
	/// </summary>
	[TestClass("Unit tests for the Method class in the SharedTypes assembly.")]
	public class MethodTests
	{
		#region Member Variables

		private static bool standardTestHasFailures = false;
		private static bool standardTestHasExceptions = false;
		private static bool standardTestExpectsException = false;
		private static bool standardTestHasConsistentException = false;

		#endregion
		
		#region Construction

		public MethodTests()
		{
		}

		#endregion

		#region Standard TestMethod Properties
		private Class StandardClass
		{
			get
			{
				Assembly A = new Assembly(UnitTestSettings.BaselineAssemblyPath);
				return new Class(this.GetType(),A);
			}
		}

		private System.Reflection.MethodInfo StandardMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "StandardTestMethod")
					{
						return m;
					}
				}

				throw new Exception("StandardMethodInfo not found.");
			}
		}

		[TestMethod("The StandardTestMethod is a test method called by this test class for testing purposes.")]
		public void StandardTestMethod(TestMethodRecord tmr)
		{
			tmr.RunTest(true, "Successful test 1.");
			tmr.RunTest(true, "Successful test 2.");
			tmr.WriteLine("This is my test test comment.");

			if (MethodTests.standardTestHasFailures)
			{
				tmr.RunTest(false, "Failed test 1.");
			}

			if (MethodTests.standardTestExpectsException)
			{
				tmr.RegisterException("Testing expected excpetion.",typeof(ArgumentNullException));
			}

			if (MethodTests.standardTestHasExceptions)
			{
				if (MethodTests.standardTestHasConsistentException)
				{
					throw new ArgumentNullException("This is a testing exception.");
				}
				else
				{
					throw new Exception("This is a testing exception.");
				}
			}
		}
		
		#endregion

		#region Signature Related TestMethod Properties

		//
		// Don't add TestMethod attributes to these methods. The tests will always fail.
		//

		public void BadSignatureTestFew()
		{
			TestMethodRecord.Current.Is.True(true);
		}

		public void BadSignatureTestMany(TestMethodRecord tmr, int AnotherArgument_BAD)
		{
		}

		public void BadSignatureTestType(object BadArgument)
		{
		}

		private System.Reflection.MethodInfo BadSignatureTestFewMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "BadSignatureTestFew")
					{
						return m;
					}
				}

				throw new Exception("BadSignatureTestFew not found.");
			}
		}

		private System.Reflection.MethodInfo BadSignatureTestManyMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "BadSignatureTestMany")
					{
						return m;
					}
				}

				throw new Exception("BadSignatureTestMany not found.");
			}
		}

		private System.Reflection.MethodInfo BadSignatureTestTypeMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "BadSignatureTestType")
					{
						return m;
					}
				}

				throw new Exception("BadSignatureTestType not found.");
			}
		}

		#endregion

		#region Ordered Methods TestMethod Properties

		private System.Reflection.MethodInfo OrderedFirstMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "OrderedFirstTestMethod")
					{
						return m;
					}
				}

				throw new Exception("OrderedFirstTestMethod not found.");
			}
		}

		private System.Reflection.MethodInfo OrderedLastMethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "OrderedLastTestMethod")
					{
						return m;
					}
				}

				throw new Exception("OrderedLastTestMethod not found.");
			}
		}

		[TestOrder(TestOrderModes.First,7)]
		public void OrderedFirstTestMethod(TestMethodRecord tmr)
		{
		}

		[TestOrder(TestOrderModes.Last,8)]
		public void OrderedLastTestMethod(TestMethodRecord tmr)
		{
		}

		[TestOrder(TestOrderModes.First,12)]
		public void OrderedFirst2(TestMethodRecord tmr)
		{
		}
	
		private System.Reflection.MethodInfo OrderedFirst2MethodInfo
		{
			get
			{
				Type type = this.GetType();
				System.Reflection.MethodInfo[] mi = type.GetMethods(System.Reflection.BindingFlags.Public |
					System.Reflection.BindingFlags.Instance);

				foreach(System.Reflection.MethodInfo m in mi)
				{
					if (m.Name == "OrderedFirst2")
					{
						return m;
					}
				}

				throw new Exception("OrderedFirst2 not found.");
			}
		}

		#endregion

		#region Construction Tests

		[TestMethod("Test the input validation for the Method class constructor.")]
		public void ConstructionTests1(TestMethodRecord tmr)
		{
			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());
			tmr.RunTest(true, "Constructor with standard objects should succeed.");
		}

		[TestMethod("Test the input validation for the Method class constructor.")]
		public void ConstructionTests2(TestMethodRecord tmr)
		{
			tmr.RegisterException("Constructor with invalid arguments throws an exception.",typeof(ArgumentNullException));
			Method m = new Method(null, StandardClass, new TestMethodAttribute());
		}

		[TestMethod("Test the input validation for the Method class constructor.")]
		public void ConstructionTests3(TestMethodRecord tmr)
		{
			tmr.RegisterException("Constructor with invalid arguments throws an exception.",typeof(ArgumentNullException));
			Method m = new Method(StandardMethodInfo, null, new TestMethodAttribute());
		}

		[TestMethod("Test the input validation for the Method class constructor.")]
		public void ConstructionTests4(TestMethodRecord tmr)
		{
			tmr.RegisterException("Constructor with invalid arguments throws an exception.",typeof(ArgumentNullException));
			Method m = new Method(StandardMethodInfo, StandardClass, null);
		}
		#endregion

		#region TestOrder Tests
		[TestMethod("Test the standard (unordered) TestOrder attributes.")]
		public void TestOrder_Standard(TestMethodRecord tmr)
		{
			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			tmr.RunTest(m.IsOrdered == false, "Standard method is not ordered.");

			tmr.RunTest(m.OrderMode == TestOrderModes.Neutral, "Order mode is neutral.");
			tmr.RunTest(m.SubModeOrder == 100, "SubModeOrder is 100.");
		}

		[TestMethod("Test the first ordered TestOrder attribute.")]
		public void TestOrder_First(TestMethodRecord tmr)
		{
			Method m = new Method(OrderedFirstMethodInfo, StandardClass, new TestMethodAttribute());

			tmr.RunTest(m.IsOrdered == true, "OrderedFirst method is ordered.");

			tmr.RunTest(m.OrderMode == TestOrderModes.First, "Order mode is First.");
			tmr.RunTest(m.SubModeOrder == 7, "SubModeOrder is 7.");
		}

		[TestMethod("Test the last ordered TestOrder attribute.")]
		public void TestOrder_Last(TestMethodRecord tmr)
		{
			Method m = new Method(OrderedLastMethodInfo, StandardClass, new TestMethodAttribute());

			tmr.RunTest(m.IsOrdered == true, "OrderedLast method is ordered.");

			tmr.RunTest(m.OrderMode == TestOrderModes.Last, "Order mode is Last.");
			tmr.RunTest(m.SubModeOrder == 8, "SubModeOrder is 8.");
		}
		#endregion

		#region Initial Value Tests
		[TestMethod("Tests the initial values of standard test method instance.")]
		public void InitialValueTest1(TestMethodRecord tmr)
		{
			string desc = "The StandardTestMethod is a test method called by this test class for testing purposes.";

			Method m = new Method(StandardMethodInfo, StandardClass,
				Attribute.GetCustomAttribute(StandardMethodInfo, typeof(TestMethodAttribute), true) as TestMethodAttribute);

			tmr.RunTest(m.Active == true, "Test is active.");
			tmr.RunTest(m.ClassName == "MethodTests", "Class name is the name of this class (its parent).");
			tmr.RunTest(m.Description == desc, "Description is the one entered in the TestMethod attribute.");
			tmr.RunTest(m.HasRun == false, "Test has not run.");
			tmr.RunTest(m.IsOrdered == false, "Method is not ordered.");
			tmr.RunTest(m.Name == "StandardTestMethod", "Name is StandardTestMethod.");
			tmr.RunTest(m.OrderMode == TestOrderModes.Neutral, "OrderMode is neutral.");
			tmr.RunTest(m.ParentClass != null, "Parent class is not null.");
			tmr.RunTest(m.SubModeOrder == 100, "SubModeOrder is 100.");
			tmr.RunTest(m.TestRecord.TestCount == 0, "TestRecord also shows no tests.");
			tmr.RunTest(m.RunningTime == 0.0, "Running time is zero.");
		}
		#endregion

		#region Method Execution Tests
		[TestMethod("Test the execution of our standard test method.")]
		public void MethodExecutionTest1(TestMethodRecord tmr)
		{
			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			tmr.RunTest(m.TestRecord.HasRun == false, "Tests have not been run on the method.");

			m.RunTest(Activator.CreateInstance(StandardClass.Type));

			tmr.RunTest(m.TestRecord.Successes == 2, "The two successful tests succeeded.");
			tmr.RunTest(m.TestRecord.TestCount == 2, "Three tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length == 0, "There was no exception.");
			tmr.RunTest(m.TestRecord.ExpectException == false, "No exception was expected.");
			tmr.RunTest(m.TestRecord.HasRun == true, "Tests have been run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 3, "Tests + comment = 3 objects.");

			m.ResetTest();

			tmr.RunTest(m.TestRecord.Successes == 0, "No tests succeeded after being reset.");
			tmr.RunTest(m.TestRecord.TestCount == 0, "Zero tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length == 0, "There was no exception.");
			tmr.RunTest(m.TestRecord.ExpectException == false, "No exception was expected.");
			tmr.RunTest(m.TestRecord.HasRun == false, "Tests have been not run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 0, "No test objects.");
		}

		[TestMethod("Test the execution of our standard test method with failures.")]
		public void MethodExecutionTest2(TestMethodRecord tmr)
		{
			MethodTests.standardTestHasFailures = true;

			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			m.RunTest(Activator.CreateInstance(StandardClass.Type));
			tmr.RunTest(m.TestRecord.Successes == 2, "The two successful tests succeeded.");
			tmr.RunTest(m.TestRecord.Failures == 1, "The failed test failed.");

			tmr.RunTest(m.TestRecord.TestCount == 3, "Three tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length == 0, "There was no exception.");
			tmr.RunTest(m.TestRecord.ExpectException == false, "No exception was expected.");
			tmr.RunTest(m.TestRecord.HasRun == true, "Tests have been run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 4, "Tests + comment = 4 objects.");

			MethodTests.standardTestHasFailures = false;
		}

		[TestMethod("Test the execution of our standard test method with failures.")]
		public void MethodExecutionTest3(TestMethodRecord tmr)
		{
			MethodTests.standardTestHasFailures = true;
			MethodTests.standardTestHasExceptions = true;

			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			m.RunTest(Activator.CreateInstance(StandardClass.Type));

			tmr.RunTest(m.TestRecord.Successes == 2, "The two successful tests succeeded.");
			tmr.RunTest(m.TestRecord.Failures == 2, "The failed test failed and an exception was thrown.");

			tmr.RunTest(m.TestRecord.TestCount == 4, "Four tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length > 0, "An exception was thrown.");
			tmr.RunTest(m.TestRecord.ExpectException == false, "No exception was expected.");
			tmr.RunTest(m.TestRecord.HasRun == true, "Tests have been run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 5, "Tests + comment + exception = 5 objects.");

			MethodTests.standardTestHasFailures = false;
			MethodTests.standardTestHasExceptions = false;
		}

		[TestMethod("Test the execution of our standard test method with failures (consistent, expected).")]
		public void MethodExecutionTest4(TestMethodRecord tmr)
		{
			MethodTests.standardTestHasFailures = true;
			MethodTests.standardTestHasExceptions = true;
			MethodTests.standardTestHasConsistentException = true;
			MethodTests.standardTestExpectsException = true;

			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			m.RunTest(Activator.CreateInstance(StandardClass.Type));

			tmr.RunTest(m.TestRecord.Successes == 3, "The two successful tests succeeded + expected exception.");
			tmr.RunTest(m.TestRecord.Failures == 1, "The failed test failed.");

			tmr.RunTest(m.TestRecord.TestCount == 4, "Four tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length > 0, "An exception was thrown.");
			tmr.RunTest(m.TestRecord.ExpectException == true, "An exception was expected.");
			tmr.RunTest(m.TestRecord.ConsistentException == true, "The exception was consistent.");
			tmr.RunTest(m.TestRecord.HasRun == true, "Tests have been run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 5, "Tests + comment + exception = 5 objects.");

			MethodTests.standardTestHasFailures = false;
			MethodTests.standardTestHasExceptions = false;
			MethodTests.standardTestHasConsistentException = false;
			MethodTests.standardTestExpectsException = false;
		}

		[TestMethod("Test the execution of our standard test method with failures (inconsistent, expected).")]
		public void MethodExecutionTest5(TestMethodRecord tmr)
		{
			MethodTests.standardTestHasFailures = true;
			MethodTests.standardTestHasExceptions = true;
			MethodTests.standardTestHasConsistentException = false;
			MethodTests.standardTestExpectsException = true;

			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			m.RunTest(Activator.CreateInstance(StandardClass.Type));

			tmr.RunTest(m.TestRecord.Successes == 2, "The two successful tests succeeded.");
			tmr.RunTest(m.TestRecord.Failures == 2, "The failed test failed + inconsistent exception.");

			tmr.RunTest(m.TestRecord.TestCount == 4, "Four tests ran total.");
			tmr.RunTest(m.TestRecord.TestExceptions.Length > 0, "An exception was thrown.");
			tmr.RunTest(m.TestRecord.ExpectException == true, "An exception was expected.");
			tmr.RunTest(m.TestRecord.ConsistentException == false, "The exception was not consistent.");
			tmr.RunTest(m.TestRecord.HasRun == true, "Tests have been run on the method.");
			tmr.RunTest(m.TestRecord.TestObjectCount == 5, "Tests + comment + exception = 5 objects.");

			MethodTests.standardTestHasFailures = false;
			MethodTests.standardTestHasExceptions = false;
			MethodTests.standardTestHasConsistentException = false;
			MethodTests.standardTestExpectsException = false;
		}
		#endregion

		#region Method Argument Tests
		[TestMethod("Test the signature of a valid test method.")]
		public void Signature_TestCorrect(TestMethodRecord tmr)
		{
			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());
			
			m.RunTest(Activator.CreateInstance(StandardClass.Type));
			tmr.RunTest(m.TestRecord.Failures == 0, "Valid test method with valid signature maked as test.");
		}

		[TestMethod("Test the signature of a method with too few arguments.")]
		public void Signature_TestTooFew(TestMethodRecord tmr)
		{
			Method m = new Method(BadSignatureTestFewMethodInfo, StandardClass, new TestMethodAttribute());
		
			m.RunTest(Activator.CreateInstance(StandardClass.Type));
			tmr.Are.Equal(1, m.TestRecord.Successes);
			tmr.Are.Equal(0, m.TestRecord.Failures);
		}

		[TestMethod("Test the signature of a method with too many arguments.")]
		public void Signature_TestTooMany(TestMethodRecord tmr)
		{
			Method m = new Method(BadSignatureTestManyMethodInfo, StandardClass, new TestMethodAttribute());
		
			m.RunTest(Activator.CreateInstance(StandardClass.Type));
			tmr.RunTest(m.TestRecord.TestExceptions.Length > 0, "Test method with invalid signature thew an exception..");
			tmr.RunTest(m.TestRecord.Successes == 0, "No tests succeeded in method.");
			tmr.RunTest(m.TestRecord.Failures == 1, "Method threw exception (hence 1 failure) due to bad signature.");
		}

		[TestMethod("Test the signature of a method with invalid argument.")]
		public void Signature_TestInvalidType(TestMethodRecord tmr)
		{
			Method m = new Method(BadSignatureTestTypeMethodInfo, StandardClass, new TestMethodAttribute());
		
			m.RunTest(Activator.CreateInstance(StandardClass.Type));
			tmr.RunTest(m.TestRecord.TestExceptions.Length > 0, "Test method with invalid signature thew an exception..");
			tmr.RunTest(m.TestRecord.Successes == 0, "No tests succeeded in method.");
			tmr.RunTest(m.TestRecord.Failures == 1, "Method threw exception (hence 1 failure) due to bad signature.");
		}
		#endregion

		#region Custom Method Sorting Tests
		[TestMethod("")]
		public void Sorting1(TestMethodRecord tmr)
		{
			Method s = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());
			Method oF1 = new Method(OrderedFirstMethodInfo, StandardClass, new TestMethodAttribute());
			Method oF2 = new Method(OrderedFirst2MethodInfo, StandardClass, new TestMethodAttribute());
			Method oL = new Method(OrderedLastMethodInfo, StandardClass, new TestMethodAttribute());

			//
			// I'm sure there are lots of ways to test the sorting, but 
			// sorting in the ArrayList is easy and quick.
			//
			ArrayList list = new ArrayList();

			list.Add(s);
			list.Add(oF2);
			list.Add(oL);
			list.Add(oF1);
			
			list.Sort();

			tmr.RunTest(list.IndexOf(oF1) == 0, "Ordered first method is first.");
			tmr.RunTest(list.IndexOf(oF2) == 1, "Ordered first - second method is second.");
			tmr.RunTest(list.IndexOf(oL) == 3, "Ordered last method is last.");
		}
		#endregion

		#region RunningTime tests
		[TestMethod("Test the running time property of the Method class.")]
		public void RunningTimeTest1(TestMethodRecord tmr)
		{
			Method m = new Method(StandardMethodInfo, StandardClass, new TestMethodAttribute());

			tmr.RunTest(m.RunningTime == 0.0, "Running time is zero.");
			m.RunningTime = 1.23456;
			tmr.RunTest(m.RunningTime == 1.23456, "Running time is 1.23456.");
		}
		#endregion
	}
}

